use crate::prelude::*;
use skia_bindings as sb;
use skia_bindings::SkString;
use std::{slice, str};

pub type String = Handle<SkString>;

impl NativeDrop for SkString {
    fn drop(&mut self) {
        unsafe {
            sb::C_SkString_destruct(self);
        }
    }
}

impl AsRef<str> for String {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl ToString for String {
    fn to_string(&self) -> std::string::String {
        self.as_str().into()
    }
}

impl Default for Handle<SkString> {
    fn default() -> Self {
        Self::from_str("")
    }
}

impl Handle<SkString> {
    pub fn from_str(str: impl AsRef<str>) -> String {
        let bytes = str.as_ref().as_bytes();
        Handle::from_native(unsafe { SkString::new3(bytes.as_ptr() as _, bytes.len()) })
    }

    pub fn as_str(&self) -> &str {
        self.native().as_str()
    }
}

pub trait AsStr {
    fn as_str(&self) -> &str;
}

impl AsStr for Handle<SkString> {
    fn as_str(&self) -> &str {
        self.native().as_str()
    }
}

impl AsStr for SkString {
    fn as_str(&self) -> &str {
        let mut size = 0;
        let slice = unsafe {
            slice::from_raw_parts(
                sb::C_SkString_c_str_size(self, &mut size) as *const u8,
                size,
            )
        };
        str::from_utf8(slice).unwrap()
    }
}

pub trait SetStr {
    fn set_str(&mut self, str: impl AsRef<str>);
}

impl SetStr for Handle<SkString> {
    fn set_str(&mut self, str: impl AsRef<str>) {
        self.native_mut().set_str(str)
    }
}

impl SetStr for SkString {
    fn set_str(&mut self, str: impl AsRef<str>) {
        let bytes = str.as_ref().as_bytes();
        unsafe { self.set1(bytes.as_ptr() as _, bytes.len()) }
    }
}

pub trait FromStrs {
    fn from_strs(strings: &[impl AsRef<str>]) -> Self;
}

impl FromStrs for Vec<String> {
    fn from_strs(strings: &[impl AsRef<str>]) -> Self {
        strings
            .iter()
            .map(|s| String::from_str(s.as_ref()))
            .collect()
    }
}

#[test]
fn string_from_rust_and_back() {
    let str = "Hello";
    let string = String::from_str(str);
    assert_eq!(str, string.as_str())
}

#[test]
fn set_string() {
    let mut hello = String::from_str("Hello");
    hello.set_str(String::from_str("World"));
    assert_eq!("World", hello.as_str());
}
